/*
 * LevelDownloadingGameStateController.cpp
 *
 *  Created on: Apr 2, 2012
 *      Author: erudhes
 */
#include <wx/socket.h>
#include <wx/dir.h>
#include <wx/filename.h>
#include <wx/gauge.h>
#include "LevelDownloadingGameStateController.h"
#include "IdleGameStateController.h"
#include "MainFrame.h"
#include "MainPanel.h"
#include "IGameController.h"
#include "Configuration.h"
#include "NetworkEventData.h"
#include "App.h"

LevelDownloadingGameStateController::LevelDownloadingGameStateController(SmartPointer<ClientStartingGameStateController> clientStartingController): mDirectoryPath(),
																																					mFileName(),
																																					mDownloadedFile(),
																																					mDownloadedFileLength(0),
																																					mDownloadedBytes(0),
																																					mIsDownloadingRejected(false),
																																					mProgressDialog(NULL),
																																					mGauge(NULL),
																																					mClientStartingController(clientStartingController)
{
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	wxChar pathSeparator = wxFileName::GetPathSeparator();
	wxString progressDialogTitle = wxT("Downloading ");

	mDirectoryPath = wxGetCwd() + pathSeparator + wxGetApp().getConfiguration()->getLevels()->getPath() + pathSeparator + wxGetApp().getActualLevel() + pathSeparator;
	mFileName = wxT("config.xml");
	wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Downloading level from the server"));
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::DOWNLOADING_START);
	data->setStringData(mFileName);
	progressDialogTitle += mFileName;
	// Create the progress dialog
	mProgressDialog = new wxDialog(NULL, wxID_ANY, progressDialogTitle);
	mProgressDialog->SetClientSize(wxSize(400, 20));
	mGauge = new wxGauge(mProgressDialog, wxID_ANY, 100, wxPoint(0, 0), wxSize(400, 20));
	mGauge->SetValue(0);
	// Send the start packet
	if(false == data->write(clientSocket))
	{
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		// Revert back to Idle state
		wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new IdleGameStateController());
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not start downloading!"), MainFrame::CLIENT_ERROR);

		return;
	}
}

/*virtual*/
LevelDownloadingGameStateController::~LevelDownloadingGameStateController()
{
	if(true == mDownloadedFile.IsOpened())
		mDownloadedFile.Close();
	if(NULL != mProgressDialog)
	{
		mProgressDialog->Destroy();
		mProgressDialog = NULL;
	}
}

//
// Methods inherited from IController
//

void LevelDownloadingGameStateController::handleMouseDown(const wxPoint&)
{
	// Nothing to do
}

void LevelDownloadingGameStateController::handleMouseUp(const wxPoint&)
{
	// Nothing to do
}

bool LevelDownloadingGameStateController::handleMouseMotion(const wxPoint&)
{
	// Nothing to do
	return false;
}

const wxBitmap& LevelDownloadingGameStateController::getBitmapToPaint() const
{
	return mLogoDrawController.getBitmapToPaint();
}

wxBitmap& LevelDownloadingGameStateController::getBitmapToDraw()
{
	return mLogoDrawController.getBitmapToDraw();
}

//
// Methods inherited from IGameStateController
//

void LevelDownloadingGameStateController::handleSocketConnection(wxSocketEvent&)
{
	wxASSERT_MSG(false, wxT("LevelDownloadingGameStateController::handleSocketConnection() invoked. It could not happen!"));
}

void LevelDownloadingGameStateController::handleSocketLost(wxSocketEvent& socketEvent)
{
	wxSocketBase* socket = wxGetApp().getClientSocket();

	if(socketEvent.GetSocket() == socket)
	{
		wxString message;

		// The remote side has been disconnected
		socket->Destroy();
		socket = NULL;
		wxGetApp().setClientSocket(NULL);
		if(true == mIsDownloadingRejected)
			message = wxT("Server most probably does not support level uploading. You can not download the level.");
		else
			message = wxT("Connection lost!");
		wxGetApp().getMainFrame()->showMessageDialog(message, MainFrame::CLIENT_ERROR);
		wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new IdleGameStateController());
	}
}

void LevelDownloadingGameStateController::handleSocketInput(wxSocketEvent& socketEvent)
{
	wxSocketBase* clientSocket = NULL;
	SmartPointer<NetworkEventData> data = NULL;
	SmartPointer<Configuration::CommonConfigurationPacket> configurationData = NULL;
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	wxChar pathSeparator = wxFileName::GetPathSeparator();
	wxString level = wxGetApp().getActualLevel();
	wxString directoryPath = wxGetCwd() + pathSeparator + wxGetApp().getConfiguration()->getLevels()->getPath() + pathSeparator + level;

	// TODO: Store the fact, that the directory is already created
	if(false == wxDir::Exists(directoryPath))
		wxMkdir(directoryPath);
	clientSocket = wxGetApp().getClientSocket();
	data = new NetworkEventData();
	// Check if an error occurred or not
	if(false == data->read(clientSocket))
	{
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxRmdir(directoryPath);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to receive data from the server"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());

		return;
	}
	switch(data->getDataType())
	{
		case NetworkEventData::DOWNLOADING_START_ACK:
			mProgressDialog->Show(true);
			mDownloadedFileLength = *((int*)(data->getRawData()));
			// The raw data in network byte order, so change it to host byte order
			mDownloadedFileLength = ntohl(mDownloadedFileLength);
			mDownloadedBytes = 0;
			break;
		case NetworkEventData::DOWNLOADING_START_REJECT:
			// The server will drop the connection, so here we only store the fact, that the server is not supporting level uploading
			mIsDownloadingRejected = true;
			break;
		case NetworkEventData::DOWNLOADING_DATA:
			this->handleDownloadedData(data);
			break;
		case NetworkEventData::DOWNLOADING_END:
			this->handleDownloadFinished();
			break;
		default:
			wxASSERT_MSG(false, wxT("Invalid message type received during downloading!"));
			break;
	}
}

void LevelDownloadingGameStateController::handleSocketOutput(wxSocketEvent&)
{
	// This method will be invoked only once, when a successful connection happens. Nothing to do yet
}

//
// Private methods
//

void LevelDownloadingGameStateController::handleDownloadedData(NetworkEventData* data)
{
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	SmartPointer<NetworkEventData> acknowledgeData = NULL;

	if(false == mDownloadedFile.IsOpened())
	{
		wxString pathToFile = mDirectoryPath + mFileName;

		mDownloadedFile.Open(pathToFile.c_str(), wxFile::write);
	}
	mDownloadedFile.Write(data->getRawData(), data->getDataLength());
	// Sending acknowledge
	acknowledgeData = new NetworkEventData();
	acknowledgeData->setDataType(NetworkEventData::DOWNLOADING_DATA_ACK);
	// Check if an error occurred or not
	if(false == acknowledgeData->write(clientSocket))
	{
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not acknowledge data receiving!"), MainFrame::CLIENT_ERROR);
		// Revert back to Idle state
		gameController->changeState(new IdleGameStateController());
	}
	mDownloadedBytes += data->getDataLength();
	mGauge->SetValue((static_cast<float>(mDownloadedBytes)/static_cast<float>(mDownloadedFileLength)) * 100);
}

void LevelDownloadingGameStateController::handleDownloadFinished()
{
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();

	mDownloadedFile.Close();
	// Figure out the next file to download
	if(wxT("config.xml") == mFileName)
		mFileName = wxGetApp().getActualLevel() + wxT(".bmp");
	else if((wxGetApp().getActualLevel() + wxT(".bmp")) == mFileName)
		mFileName = wxT("thumbnails.bmp");
	else if(wxT("thumbnails.bmp") == mFileName)
		mFileName = wxT("not_free_thumbnails.bmp");
	else if(wxT("not_free_thumbnails.bmp") == mFileName)
	{
		// We downloaded all the needed files - ready to start the game itself
		data = new NetworkEventData();
		data->setDataType(NetworkEventData::CONNECTING_ACK);
		if(false == data->write(clientSocket))
		{
			clientSocket->Destroy();
			clientSocket = NULL;
			wxGetApp().setClientSocket(NULL);
			wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not acknowledge connecting after level download!"), MainFrame::CLIENT_ERROR);
			// Revert back to Idle state
			gameController->changeState(new IdleGameStateController());

			return;
		}
		// Add the new level to the Configuration as well
		wxGetApp().getConfiguration()->getLevels()->addNewLevel(wxGetApp().getActualLevel());
		// CLose the progress dialog
		mGauge->SetValue(100);
		mProgressDialog->Close(true);
		mProgressDialog->Destroy();
		mProgressDialog = NULL;
		mGauge = NULL;
		// Switch back to ClientStarting state to handle the configuration negotiation
		gameController->changeState(mClientStartingController);

		return;
	}
	else
		wxASSERT_MSG(false, wxT("Invalid mFileName!"));
	// Continue with downloading the next needed file
	mProgressDialog->SetTitle(wxT("Downloading ") + mFileName);
	mGauge->SetValue(0);
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::DOWNLOADING_START);
	data->setStringData(mFileName);
	if(false == data->write(clientSocket))
	{
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		// Revert back to Idle state
		wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new IdleGameStateController());
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Error during downloading level"), MainFrame::CLIENT_ERROR);
	}
}
